﻿using System;
using System.Collections.Generic;
using System.Linq;
using Assist;
using Autodesk.Revit.DB;
using Autodesk.Revit.UI;
using Autodesk.Revit.UI.Selection;
using YArchitech.Revit;
using YArchitech.Revit.RevitExtension;
using HYElectricKernel;

namespace HYRElectric.EquipmentPlace
{
	public class EquipmentModifyer
	{
		public double HDistance { get; set; }

		public double VDistance { get; set; }

		public EquipmentType equipmentType { get; set; }

		public EquipmentModifyer(ExternalCommandData cmdData)
		{
			this.m_Revit = cmdData;
			this.m_Doc = this.m_Revit.Application.ActiveUIDocument.Document;
			this.HDistance = 0.0;
			this.VDistance = 0.0;
		}

		public Result FindSingle()
		{
			FamilyInstance eauipmentEle;
			while ((eauipmentEle = this.SelectEauipment("选择需要移动的一个实体")) != null)
			{
				this.MoveEauipment(eauipmentEle);
			}
			return Autodesk.Revit.UI.Result.Succeeded;
		}

		public Result FindCurrentFloor(BuiltInCategory builtInCategory)
		{
            if (builtInCategory == (BuiltInCategory)(-1))
			{
				FamilyInstance familyInstance = this.SelectEauipment("选择需要移动的同类实体中的一个");
				if (familyInstance == null)
				{
					return Autodesk.Revit.UI.Result.Cancelled;
				}
                builtInCategory = (BuiltInCategory)familyInstance.Category.Id.IntegerValue;
			}
			foreach (FamilyInstance eauipmentEle in this.GetAllInstacesInView(this.m_Doc, builtInCategory, this.m_Doc.ActiveView))
			{
				this.MoveEauipment(eauipmentEle);
			}
			return Autodesk.Revit.UI.Result.Succeeded;
		}

		public Result FindDocument(BuiltInCategory builtInCategory)
		{
            if (builtInCategory == (BuiltInCategory)(-1))
			{
				FamilyInstance familyInstance = this.SelectEauipment("选择需要移动的同类实体中的一个");
				if (familyInstance == null)
				{
					return Autodesk.Revit.UI.Result.Cancelled;
				}
                builtInCategory = (BuiltInCategory)familyInstance.Category.Id.IntegerValue;
			}
			foreach (FamilyInstance eauipmentEle in this.GetAllInstacesInDoc(this.m_Doc, builtInCategory))
			{
				this.MoveEauipment(eauipmentEle);
			}
			return Autodesk.Revit.UI.Result.Succeeded;
		}

		private Result MoveEauipment(FamilyInstance eauipmentEle)
		{
			XYZ point = (eauipmentEle.Location as LocationPoint).Point;
			Element element = null;
			try
			{
				element = this.FindFamilyInstanceHost(eauipmentEle);
			}
			catch (Exception)
			{
				return Autodesk.Revit.UI.Result.Failed;
			}
			if (element == null)
			{
				foreach (RevitLinkInstance linkInstance in this.GetAllRevitLinkInstancesInDoc(this.m_Doc))
				{
					if (this.OperateLinkInstance(linkInstance, eauipmentEle))
					{
						return Autodesk.Revit.UI.Result.Succeeded;
					}
				}
				return Autodesk.Revit.UI.Result.Failed;
			}
			if (element.GetType() == typeof(RevitLinkInstance))
			{
				RevitLinkInstance linkInstance2 = element as RevitLinkInstance;
				if (!this.OperateLinkInstance(linkInstance2, eauipmentEle))
				{
					return Autodesk.Revit.UI.Result.Failed;
				}
				return Autodesk.Revit.UI.Result.Succeeded;
			}
			else
			{
				if (!(element.GetType() == typeof(Wall)))
				{
					return Autodesk.Revit.UI.Result.Failed;
				}
				double elevation = 0.0;
				if (!this.InstanceLevelElevation(eauipmentEle, ref elevation))
				{
					return Autodesk.Revit.UI.Result.Failed;
				}
				ViewPlan floorViewPlanByElevation = this.GetFloorViewPlanByElevation(this.m_Doc, elevation);
				if (floorViewPlanByElevation == null)
				{
					return Autodesk.Revit.UI.Result.Failed;
				}
				EdgeInfo hDistance = null;
				double vDistance = 0.0;
				Wall wall = null;
				FamilyInstance familyInstance = this.FindDoorInDocument(point, this.m_Doc, floorViewPlanByElevation, ref wall, ref hDistance, ref vDistance);
				if (familyInstance == null)
				{
					return Autodesk.Revit.UI.Result.Failed;
				}
				this.MoveOneEauipment(familyInstance, wall, eauipmentEle, hDistance, vDistance);
				return Autodesk.Revit.UI.Result.Succeeded;
			}
			Result result;
			return result;
		}

		private bool OperateLinkInstance(RevitLinkInstance linkInstance, FamilyInstance eauipmentEle)
		{
			if (linkInstance == null)
			{
				return false;
			}
			XYZ point = (eauipmentEle.Location as LocationPoint).Point;
			Document linkDocument = linkInstance.GetLinkDocument();
			if (linkDocument == null)
			{
				return false;
			}
			double elevation = 0.0;
			if (!this.InstanceLevelElevation(eauipmentEle, ref elevation))
			{
				return false;
			}
			ViewPlan floorViewPlanByElevation = this.GetFloorViewPlanByElevation(linkDocument, elevation);
			if (floorViewPlanByElevation == null)
			{
				return false;
			}
			Transform transform = linkInstance.GetTransform();
			XYZ ptLocal = Geometry.TransformPoint(point, transform.Inverse);
			EdgeInfo edgeInfo = null;
			double vDistance = 0.0;
			Wall wall = null;
			FamilyInstance familyInstance = this.FindDoorInDocument(ptLocal, linkDocument, floorViewPlanByElevation, ref wall, ref edgeInfo, ref vDistance);
			if (familyInstance == null)
			{
				return false;
			}
			edgeInfo.TransformSelf(transform);
			if (!edgeInfo.IsArc)
			{
				edgeInfo = new EdgeInfo(XYZ.Zero, edgeInfo.EndPoint - edgeInfo.StartPoint, 0.0);
			}
			this.MoveOneEauipment(familyInstance, wall, eauipmentEle, edgeInfo, vDistance);
			return true;
		}

		private bool InstanceLevelElevation(FamilyInstance instance, ref double elevation)
		{
			ElementId elementId = instance.get_Parameter(BuiltInParameter.SCHEDULE_LEVEL_PARAM).AsElementId();
			if (elementId == ElementId.InvalidElementId)
			{
				return false;
			}
			Level level = this.m_Doc.GetElement(elementId) as Level;
			elevation = level.Elevation;
			return true;
		}

		private Element FindFamilyInstanceHost(FamilyInstance familyInstance)
		{
			if (familyInstance.Host != null)
			{
				return familyInstance.Host;
			}
			XYZ point = (familyInstance.Location as LocationPoint).Point;
			double elevation = 0.0;
			if (!this.InstanceLevelElevation(familyInstance, ref elevation))
			{
				throw new Exception("level is null");
			}
			ViewPlan floorViewPlanByElevation = this.GetFloorViewPlanByElevation(this.m_Doc, elevation);
			if (floorViewPlanByElevation == null)
			{
				throw new Exception("not find viewplace");
			}
			Element result = null;
			Wall result2 = null;
			if (this.FindedWallByLocation(point, this.m_Doc, floorViewPlanByElevation, ref result2))
			{
				return result2;
			}
			return result;
		}

		private void MoveOneEauipment(FamilyInstance door, Wall wall, FamilyInstance eauipmentEle, EdgeInfo hDistance, double vDistance)
		{
			double num = hDistance.Length();
			num = Common.FeetToMM(num);
			double num2 = 0.0;
			if (this.equipmentType == EquipmentType.ET_Switch)
			{
				num2 = Common.FeetToMM(door.Symbol.get_Parameter(BuiltInParameter.CASEWORK_WIDTH).AsDouble());
			}
			num -= num2 / 2.0;
			if (!Geometry.IsEqual(num, this.HDistance) || !Geometry.IsEqual(vDistance, this.VDistance))
			{
				Transaction transaction = new Transaction(this.m_Doc);
				transaction.Start("move a eauipment");
				if (hDistance.IsArc)
				{
					XYZ center = hDistance.Center;
					XYZ xyz = XYZ.BasisZ;
					if (Geometry.LessThan(hDistance.Bulge, 0.0))
					{
						xyz = -XYZ.BasisZ;
					}
					double num3 = Common.MMToFeet(num - this.HDistance) / hDistance.Radius;
					if (eauipmentEle.Symbol.Family.GetParameter(BuiltInParameter.FAMILY_HOSTING_BEHAVIOR).AsInteger() == 0)
					{
						Line line = YJKLineEx.YJKGetUnBound(center, xyz);
						ElementTransformUtils.RotateElement(this.m_Doc, eauipmentEle.Id, line, num3);
					}
					else
					{
						Transform transform = RevitVersionFuncs.CreateRotationAtPoint(xyz, num3, center);
						XYZ point = (eauipmentEle.Location as LocationPoint).Point;
						XYZ xyz2 = Geometry.TransformPoint(point, transform) - point;
						ElementTransformUtils.MoveElement(this.m_Doc, eauipmentEle.Id, xyz2);
					}
				}
				else
				{
					XYZ xyz3 = hDistance.Normal * Common.MMToFeet(num - this.HDistance);
					ElementTransformUtils.MoveElement(this.m_Doc, eauipmentEle.Id, xyz3);
				}
				if (this.equipmentType == EquipmentType.ET_EmergencyLight)
				{
					double num4 = vDistance - Common.MMToFeet(this.VDistance);
					Parameter parameter = eauipmentEle.get_Parameter(BuiltInParameter.INSTANCE_ELEVATION_PARAM);
					parameter.Set(parameter.AsDouble() - num4);
				}
				transaction.Commit();
			}
		}

		private FamilyInstance SelectEauipment(string statusPrompt)
		{
			FamilyInstance result = null;
			Selection selection = this.m_Revit.Application.ActiveUIDocument.Selection;
			try
			{
				Reference reference;
				if (this.equipmentType == EquipmentType.ET_Switch)
				{
					reference = selection.PickObject(Autodesk.Revit.UI.Selection.ObjectType.Element, new SwitchFilter(), statusPrompt);
				}
				else
				{
					reference = selection.PickObject(Autodesk.Revit.UI.Selection.ObjectType.Element, new EmergencyLightFilter(), statusPrompt);
				}
				result = (this.m_Doc.GetElement(reference) as FamilyInstance);
			}
			catch (Exception)
			{
				return result;
			}
			return result;
		}

		private FamilyInstance FindDoorInDocument(XYZ ptLocal, Document linkDoc, Autodesk.Revit.DB.View view, ref Wall wall, ref EdgeInfo hDistance, ref double vDistance)
		{
			if (!this.FindedWallByLocation(ptLocal, linkDoc, view, ref wall))
			{
				return null;
			}
			List<FamilyInstance> list = new List<FamilyInstance>();
			this.FindedDoorsByWall(wall, linkDoc, view, ref list);
			FamilyInstance familyInstance = null;
			double val = double.MaxValue;
			foreach (FamilyInstance familyInstance2 in list)
			{
				double num = (familyInstance2.Location as LocationPoint).Point.DistanceTo(ptLocal);
				if (Geometry.LessThan(num, val))
				{
					val = num;
					familyInstance = familyInstance2;
				}
			}
			if (familyInstance == null)
			{
				return null;
			}
			XYZ pt = (familyInstance.Location as LocationPoint).Point;
			pt = this.CalculateFootOnWallCenter(wall, pt);
			XYZ pt2 = this.CalculateFootOnWallCenter(wall, ptLocal);
			ElementId elementId = familyInstance.get_Parameter(BuiltInParameter.FAMILY_LEVEL_PARAM).AsElementId();
			Level level = linkDoc.GetElement(elementId) as Level;
			double num2 = familyInstance.get_Parameter(BuiltInParameter.INSTANCE_HEAD_HEIGHT_PARAM).AsDouble();
			double num3 = level.Elevation + num2;
			vDistance = ptLocal.Z - num3;
			Curve curve = (wall.Location as LocationCurve).Curve;
			if (curve.GetType() == typeof(Line))
			{
				hDistance = new EdgeInfo(this.To2d(pt2), this.To2d(pt), 0.0);
			}
			else
			{
				Arc arc = curve as Arc;
				XYZ ptSOfArc = this.To2d(arc.GetEndPoint(0));
				XYZ ptEOfArc = this.To2d(arc.GetEndPoint(1));
				XYZ xyz = this.To2d(pt2);
				XYZ xyz2 = this.To2d(pt);
				XYZ xyz3 = this.To2d(arc.Center);
				bool flag = false;
				if (arc.Normal.IsAlmostEqualTo(XYZ.BasisZ))
				{
					flag = true;
				}
				Geometry.getAngle(xyz3, xyz);
				Geometry.getAngle(xyz3, xyz2);
				XYZ pt3 = Geometry.CalculatMidPoint(xyz, xyz2, xyz3, arc.Normal);
				if (Geometry.Is_Point_OnSegment(ptSOfArc, ptEOfArc, xyz3, arc.Normal, pt3, 1E-09))
				{
					double bulge = Geometry.GetBulge(xyz, xyz2, xyz3, flag);
					hDistance = new EdgeInfo(xyz, xyz2, bulge);
				}
				else
				{
					double bulge2 = Geometry.GetBulge(xyz, xyz2, xyz3, !flag);
					hDistance = new EdgeInfo(xyz, xyz2, bulge2);
				}
			}
			return familyInstance;
		}

		private void FindedDoorsByWall(Wall wall, Document linkDoc, Autodesk.Revit.DB.View view, ref List<FamilyInstance> findedDoors)
		{
			foreach (FamilyInstance familyInstance in this.GetAllDoorsInDoc(linkDoc, view))
			{
				if (familyInstance.Host.Id.IntegerValue == wall.Id.IntegerValue)
				{
					findedDoors.Add(familyInstance);
				}
			}
		}

		private bool FindedWallByLocation(XYZ ptLocal, Document doc, Autodesk.Revit.DB.View view, ref Wall findedWall)
		{
			foreach (Wall wall in this.GetAllWallsInDoc(doc, view))
			{
				Curve curve = (wall.Location as LocationCurve).Curve;
				if (curve.GetType() == typeof(Line))
				{
					Line line = curve as Line;
					XYZ pt = Geometry.CalculateFootPoint(line, ptLocal);
					if (Geometry.Is_Point_OnSegment(line.GetEndPoint(0), line.GetEndPoint(1), pt, 1E-09) && Geometry.LessThan(this.To2d(pt).DistanceTo(this.To2d(ptLocal)), wall.Width / 2.0 + Common.MMToFeet(30.0)))
					{
						findedWall = wall;
						return true;
					}
				}
				else if (curve.GetType() == typeof(Arc))
				{
					Arc arc = curve as Arc;
					XYZ pt2 = Geometry.CalculateFootPoint(arc, ptLocal);
					if (Geometry.Is_Point_OnSegment(arc.GetEndPoint(0), arc.GetEndPoint(1), arc.Center, arc.Normal, pt2, 1E-09) && Geometry.LessThan(this.To2d(pt2).DistanceTo(this.To2d(ptLocal)), wall.Width / 2.0 + Common.MMToFeet(30.0)))
					{
						findedWall = wall;
						return true;
					}
				}
			}
			return false;
		}

		private XYZ CalculateFootOnWallCenter(Wall wall, XYZ pt)
		{
			XYZ result = null;
			Curve curve = (wall.Location as LocationCurve).Curve;
			if (curve.GetType() == typeof(Line))
			{
				result = Geometry.CalculateFootPoint(curve as Line, pt);
			}
			else if (curve.GetType() == typeof(Arc))
			{
				result = Geometry.CalculateFootPoint(curve as Arc, pt);
			}
			return result;
		}

		private XYZ To2d(XYZ pt)
		{
			return new XYZ(pt.X, pt.Y, 0.0);
		}

		private List<FamilyInstance> GetAllInstacesInView(Document doc, BuiltInCategory builtInCategory, Autodesk.Revit.DB.View view)
		{
			IEnumerable<ElementId> enumerable = new FilteredElementCollector(doc, view.Id).OfClass(typeof(FamilyInstance)).OfCategory(builtInCategory).ToElementIds();
			List<FamilyInstance> list = new List<FamilyInstance>();
			foreach (ElementId elementId in enumerable)
			{
				Element element = doc.GetElement(elementId);
				if (!(element.GetType() != typeof(FamilyInstance)))
				{
					FamilyInstance familyInstance = element as FamilyInstance;
					if (familyInstance != null)
					{
						string familyName = familyInstance.Symbol.FamilyName;
						if (!string.IsNullOrEmpty(familyName) && (familyInstance.Category.Id.IntegerValue != (int)BuiltInCategory.OST_LightingFixtures || familyName.Contains(EquipmentModifyer.m_strFamilyName)))
						{
							list.Add(familyInstance);
						}
					}
				}
			}
			return list;
		}

		private List<FamilyInstance> GetAllInstacesInDoc(Document doc, BuiltInCategory builtInCategory)
		{
			IEnumerable<ElementId> enumerable = new FilteredElementCollector(doc).OfClass(typeof(FamilyInstance)).OfCategory(builtInCategory).ToElementIds();
			List<FamilyInstance> list = new List<FamilyInstance>();
			foreach (ElementId elementId in enumerable)
			{
				Element element = doc.GetElement(elementId);
				if (!(element.GetType() != typeof(FamilyInstance)))
				{
					FamilyInstance familyInstance = element as FamilyInstance;
					if (familyInstance != null)
					{
						string familyName = familyInstance.Symbol.FamilyName;
                        if (!string.IsNullOrEmpty(familyName) && (familyInstance.Category.Id.IntegerValue != (int)BuiltInCategory.OST_LightingFixtures || familyName.Contains(EquipmentModifyer.m_strFamilyName)))
						{
							list.Add(familyInstance);
						}
					}
				}
			}
			return list;
		}

		private List<FamilyInstance> GetAllDoorsInDoc(Document doc, Autodesk.Revit.DB.View view)
		{
			IEnumerable<ElementId> enumerable = new FilteredElementCollector(doc, view.Id).OfClass(typeof(FamilyInstance)).OfCategory(BuiltInCategory.OST_Doors).ToElementIds();
			List<FamilyInstance> list = new List<FamilyInstance>();
			foreach (ElementId elementId in enumerable)
			{
				Element element = doc.GetElement(elementId);
				if (!(element.GetType() != typeof(FamilyInstance)))
				{
					FamilyInstance familyInstance = element as FamilyInstance;
					if (familyInstance != null)
					{
						list.Add(familyInstance);
					}
				}
			}
			return list;
		}

		private List<RevitLinkInstance> GetAllRevitLinkInstancesInDoc(Document doc)
		{
			IEnumerable<ElementId> enumerable = new FilteredElementCollector(doc).OfClass(typeof(RevitLinkInstance)).ToElementIds();
			List<RevitLinkInstance> list = new List<RevitLinkInstance>();
			foreach (ElementId elementId in enumerable)
			{
				Element element = doc.GetElement(elementId);
				if (!(element.GetType() != typeof(RevitLinkInstance)))
				{
					RevitLinkInstance revitLinkInstance = element as RevitLinkInstance;
					if (revitLinkInstance != null)
					{
						list.Add(revitLinkInstance);
					}
				}
			}
			return list;
		}

		private List<Wall> GetAllWallsInDoc(Document doc, Autodesk.Revit.DB.View view)
		{
			IEnumerable<ElementId> enumerable = new FilteredElementCollector(doc, view.Id).OfClass(typeof(Wall)).ToElementIds();
			List<Wall> list = new List<Wall>();
			foreach (ElementId elementId in enumerable)
			{
				Element element = doc.GetElement(elementId);
				if (!(element.GetType() != typeof(Wall)))
				{
					Wall wall = element as Wall;
					if (wall != null)
					{
						list.Add(wall);
					}
				}
			}
			return list;
		}

		private ViewPlan GetFloorViewPlanByElevation(Document doc, double elevation)
		{
			if (doc == null)
			{
				return null;
			}
			IEnumerable<ElementId> enumerable = new FilteredElementCollector(doc).OfClass(typeof(View)).ToElementIds();
			List<ViewPlan> list = new List<ViewPlan>();
			foreach (ElementId elementId in enumerable)
			{
				Element element = doc.GetElement(elementId);
				if (!(element.GetType() != typeof(ViewPlan)))
				{
					ViewPlan viewPlan = element as ViewPlan;
					if (viewPlan.ViewType == (ViewType)1 && !viewPlan.IsTemplate && viewPlan.Name.CompareTo("场地") != 0 && Geometry.IsEqual(viewPlan.GenLevel.Elevation, elevation))
					{
						list.Add(viewPlan);
					}
				}
			}
			foreach (ViewPlan viewPlan2 in list)
			{
				if (viewPlan2.Id.IntegerValue == this.m_Doc.ActiveView.Id.IntegerValue)
				{
					return viewPlan2;
				}
			}
			if (list.Count > 0)
			{
				return list.ElementAt(0);
			}
			return null;
		}

		private ExternalCommandData m_Revit;

		private Document m_Doc;

		private static string m_strFamilyName = "安全出口";
	}
}
